package service

import (
	"errors"
	"time"

	"github.com/dgrijalva/jwt-go"
	logger "github.com/sirupsen/logrus"
	"golang.org/x/crypto/bcrypt"

	"publish-manage/internal/model"
	"publish-manage/pkg/constant"
	"publish-manage/pkg/database"
	"publish-manage/pkg/domain"
	"publish-manage/pkg/util"
)

var UserService = new(userService)

type userService struct{}

//Add 新增用户，如果有密码则对密码进行加密
func (s *userService) Add(instance *model.User) (isDuplicated bool, err error) {
	if instance.Username == "" || instance.Password == "" {
		return false, errors.New("用户名密码不能为空")
	}
	var c int64 = 0
	c, err = database.DB.Count(&model.User{Username: instance.Username})
	if err != nil {
		return
	}
	if c > 0 {
		isDuplicated = true
		return
	}

	instance.Id = util.GenerateDatabaseID()
	pwd, _ := bcrypt.GenerateFromPassword([]byte(instance.Password), bcrypt.DefaultCost)

	instance.Password = string(pwd)
	instance.Status = 1

	_, err = database.DB.Insert(instance)
	if err != nil {
		return
	}
	instance.Password = "" // 最后置空密码
	return
}

//Update 更新用户信息,通过ID更新
func (_ *userService) Update(instance *model.User) error {
	if instance.Id == "" {
		return errors.New("ID不能为空")
	}
	// 不允许更改的字段
	if instance.Username != "" {
		instance.Username = ""
	}
	if instance.Password != "" {
		instance.Password = ""
	}
	old := &model.User{Id: instance.Id}
	if exist, err := database.DB.Get(old); err != nil {
		return err
	} else if !exist {
		return nil
	}
	_, err := database.DB.ID(instance.Id).Update(instance)
	if err != nil {
		logger.Error(err)
		return err
	}

	if err != nil {
		return err
	}
	return nil
}

func (_ *userService) UpdatePassword(instance *model.User, newPassword string) (bool, error) {
	if instance.Id == "" {
		return false, errors.New("ID不能为空")
	}

	u := new(model.User)
	if exist, err := database.DB.ID(instance.Id).Get(u); err != nil {
		return false, err
	} else if !exist {
		return false, nil
	}
	if err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(instance.Password)); err != nil {
		return false, nil
	}
	// 更新
	nPwd, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
	if err != nil {
		return false, err
	}

	var c int64
	c, err = database.DB.ID(instance.Id).Update(&model.User{
		Password: string(nPwd),
	})
	if err != nil {
		return false, err
	}
	return c > 0, nil
}

func (_ *userService) ResetPassword(userId, newPassword string) (bool, error) {
	if userId == "" {
		return false, errors.New("ID不能为空")
	}

	// 更新
	nPwd, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
	if err != nil {
		return false, err
	}

	var c int64
	c, err = database.DB.ID(userId).Update(&model.User{
		Password: string(nPwd),
	})
	if err != nil {
		return false, err
	}
	return c > 0, nil
}

func (_ *userService) Delete(id string) error {
	if id == "" {
		return errors.New("id不能为空")
	}

	session := database.DB.NewSession()
	defer session.Close()
	session.Begin()

	if _, err := session.Delete(&model.User{Id: id}); err != nil {
		return err
	}
	if _, err := session.Delete(&model.AppPackage{AppId: id}); err != nil {
		return err
	}
	if _, err := session.Delete(&model.PackageVersion{AppId: id}); err != nil {
		return err
	}
	return session.Commit()
}

func (_ *userService) Get(instance *model.User) (*model.User, error) {
	if instance.Id == "" {
		return nil, errors.New("id不能为空")
	}

	if exist, err := database.DB.Get(instance); err != nil {
		return nil, err
	} else if !exist {
		return nil, nil
	}
	return instance, nil
}

func (_ *userService) List(condition *model.User, offset, limit int, orderBy string) (pageInfo *domain.Paginate, err error) {
	// 处理不允许查询的字段

	// 处理sql
	session := database.DB.NewSession()
	if limit > -1 && offset > -1 {
		session.Limit(limit, offset)
	}

	if orderBy != "" {
		session.OrderBy(orderBy)
	}
	session.Desc("create_time")

	// 模糊查找
	if condition.Username != "" {
		session.Where("username like ?", condition.Username+"%")
		condition.Username = ""
	}
	if condition.RealName != "" {
		session.Where("real_name like ?", condition.RealName+"%")
		condition.RealName = ""
	}

	var list []*model.User
	total, err := session.FindAndCount(&list, condition)
	if err != nil {
		return nil, err
	}

	return &domain.Paginate{
		Total:  int(total),
		Offset: offset,
		Limit:  limit,
		Items:  list,
	}, nil
}

func (_ *userService) Login(instance *model.User) (*model.User, string, error) {
	if instance.Username == "" {
		return nil, "", errors.New("用户名不能为空")
	}
	u := new(model.User)
	u.Username = instance.Username
	has, err := database.DB.Get(u)
	if err != nil {
		return nil, "", err
	}

	if has {
		err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(instance.Password))
		if err != nil {
			return nil, "", err
		}
		token := ""
		token, err = generateToken(u.Id, u.Username, constant.TokenExpireTime)
		if err != nil {
			return nil, "", err
		}

		u.Password = ""
		return u, token, nil
	}
	return nil, "", nil
}

func generateToken(userId string, username string, expireInSecond int) (string, error) {
	token := jwt.New(jwt.SigningMethodHS256)
	sid := util.GenerateDatabaseID()

	claims := token.Claims.(jwt.MapClaims)
	claims["username"] = username
	claims["uid"] = userId
	claims["sid"] = sid
	claims["exp"] = time.Now().Add(time.Second * time.Duration(expireInSecond)).Unix()

	t, err := token.SignedString([]byte(constant.JWT_SECRET))
	return t, err
}
